package com.template.utils.redis;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.QueryTimeoutException;
import org.springframework.data.redis.connection.RedisListCommands;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.time.Duration;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Component
public class ListOps extends KeyOps {

    private final ListOperations<String, Object> listOperations;

    @Autowired
    public ListOps(RedisTemplate<String, Object> redisTemplate) {
        super(redisTemplate);
        this.listOperations = redisTemplate.opsForList();
    }


    /**
     * <h4> List </h4>
     * 批量添加
     * <li> lpush _key _value1 _value2 ... </li>
     *
     * @return 添加成功后的list总元素个数
     */
    public Long lPush(String key, Object... values) {
        if (values == null || values.length == 0) return 0L;
        if (values.length == 1) {
            return listOperations.leftPush(key, values[0]);
        }
        return listOperations.leftPushAll(key, values);
    }

    /**
     * <h4> List </h4>
     * 批量添加
     * <li> lpush _key _value1 _value2 ... </li>
     *
     * @return 添加成功后的list总元素个数
     */
    public Long lPush(String key, Collection<Object> values) {
        return listOperations.leftPushAll(key, values);
    }

    /**
     * <h4> List </h4>
     * 仅当 key 存在时才添加
     * <li> lpushx _key _value </li>
     *
     * @return 添加成功后的list总元素个数
     */
    public Long lPushX(String key, Object value) {
        return listOperations.leftPushIfPresent(key, value);
    }

    /**
     * <h4> List </h4>
     * 批量添加
     * <li> rpush _key _value1 _value2 ... </li>
     *
     * @return 添加成功后的list总元素个数
     */
    public Long rPush(String key, Object... values) {
        if (values == null || values.length == 0) return 0L;
        if (values.length == 1) {
            return listOperations.rightPush(key, values[0]);
        }
        return listOperations.rightPushAll(key, values);
    }

    /**
     * <h4> List </h4>
     * 批量添加
     * <li> rpush _key _value1 _value2 ... </li>
     *
     * @return 添加成功后的list总元素个数
     */
    public Long rPush(String key, Collection<Object> values) {
        return listOperations.rightPushAll(key, values);
    }

    /**
     * <h4> List </h4>
     * 仅当 key 存在时才添加
     * <li> rpushx _key _value </li>
     *
     * @return 添加成功后的list总元素个数
     */
    public Long rPushX(String key, Object value) {
        return listOperations.rightPushIfPresent(key, value);
    }

    /**
     * <h4> List </h4>
     * 在 pivot 之前插入 value
     * <li> linsert _key before _pivot _value </li>
     */
    public Long lInsertBefore(String key, Object pivot, Object value) {
        return listOperations.leftPush(key, pivot, value);
    }

    /**
     * <h4> List </h4>
     * 在 pivot 之后插入 value
     * <li> linsert _key after _pivot _value </li>
     */
    public Long lInsertAfter(String key, Object pivot, Object value) {
        return listOperations.rightPush(key, pivot, value);
    }

    /**
     * <h4> List </h4>
     * 弹出 key1 右边的值并放入到 key2 的左边（ 6.2.0 之后的版本被视为弃用，请使用move替代）
     * <li> rpoplpush _key1 _key2 </li>
     */
    @Deprecated
    public Object rPopLPush(String key1, String key2) {
        return listOperations.rightPopAndLeftPush(key1, key2);
    }

    /**
     * <h4> List </h4>
     * 弹出 key1 右边的值并放入到 key2 的左边，若为空将会阻塞并等待其他客户端放入，超时后抛出异常（ 6.2.0 之后的版本被视为弃用，请使用move替代）
     * <li> brpoplpush _key1 _key2 _timeout </li>
     *
     * @throws QueryTimeoutException 超时
     */
    @Deprecated
    public Object bRPopLPush(String key1, String key2, long timeout) {
        return listOperations.rightPopAndLeftPush(key1, key2, timeout, TimeUnit.SECONDS);
    }

    /**
     * <h4> List </h4>
     * 弹出 key1 右边的值并放入到 key2 的左边，若为空将会阻塞并等待其他客户端放入，超时后抛出异常（ 6.2.0 之后的版本被视为弃用，请使用move替代）
     * <li> brpoplpush _key1 _key2 _timeout </li>
     *
     * @throws QueryTimeoutException 超时
     */
    @Deprecated
    public Object bRPopLPush(String key1, String key2, Duration timeout) {
        return listOperations.rightPopAndLeftPush(key1, key2, timeout);
    }

    /**
     * <h4> List </h4>
     * 移动并获取 value
     * <li> lmove _formKey _toKey _formDict _toDict </li>
     */
    public Object lMove(org.springframework.data.redis.core.ListOperations.MoveFrom<String> moveFrom, org.springframework.data.redis.core.ListOperations.MoveTo<String> moveTo) {
        return listOperations.move(moveFrom, moveTo);
    }

    /**
     * <h4> List </h4>
     * 移动并获取 value
     * <li> lmove _formKey _toKey _formDict _toDict </li>
     */
    public Object lMove(String formKey, String toKey, RedisListCommands.Direction formDict, RedisListCommands.Direction toDict) {
        return listOperations.move(formKey, formDict, toKey, toDict);
    }

    /**
     * <h4> List </h4>
     * 移动并获取 value，若为空将会阻塞并等待其他客户端放入，超时后抛出异常
     * <li> blmove _formKey _toKey _formDict _toDict </li>
     *
     * @throws QueryTimeoutException 超时
     */
    public Object bLMove(org.springframework.data.redis.core.ListOperations.MoveFrom<String> moveFrom, org.springframework.data.redis.core.ListOperations.MoveTo<String> moveTo, long timeout) {
        return listOperations.move(moveFrom, moveTo, Duration.ofSeconds(timeout));
    }

    /**
     * <h4> List </h4>
     * 移动并获取 value，若为空将会阻塞并等待其他客户端放入，超时后抛出异常
     * <li> blmove _formKey _toKey _formDict _toDict </li>
     *
     * @throws QueryTimeoutException 超时
     */
    public Object bLMove(org.springframework.data.redis.core.ListOperations.MoveFrom<String> moveFrom, org.springframework.data.redis.core.ListOperations.MoveTo<String> moveTo, Duration timeout) {
        return listOperations.move(moveFrom, moveTo, timeout);
    }

    /**
     * <h4> List </h4>
     * 移动并获取 value，若为空将会阻塞并等待其他客户端放入，超时后抛出异常
     * <li> blmove _formKey _toKey _formDict _toDict _timeout </li>
     *
     * @throws QueryTimeoutException 超时
     */
    public Object bLMove(String formKey, String toKey, RedisListCommands.Direction formDict, RedisListCommands.Direction toDict, long timeout) {
        return listOperations.move(formKey, formDict, toKey, toDict, timeout, TimeUnit.SECONDS);
    }

    /**
     * <h4> List </h4>
     * 移动并获取 value，若为空将会阻塞并等待其他客户端放入，超时后抛出异常
     * <li> blmove _formKey _toKey _formDict _toDict _timeout </li>
     *
     * @throws QueryTimeoutException 超时
     */
    public Object bLMove(String formKey, String toKey, RedisListCommands.Direction formDict, RedisListCommands.Direction toDict, Duration timeout) {
        return listOperations.move(formKey, formDict, toKey, toDict, timeout);
    }

    /**
     * <h4> List </h4>
     * 弹出一个左边的值
     * <li> lpop _key </li>
     */
    public Object lPop(String key) {
        return listOperations.leftPop(key);
    }

    /**
     * <h4> List </h4>
     * 弹出多个左边的值
     * <li> lpop _key </li>
     */
    public Object lPop(String key, long count) {
        return listOperations.leftPop(key, count);
    }

    /**
     * <h4> List </h4>
     * 弹出多个左边的值，若为空将会阻塞并等待其他客户端放入，超时后抛出异常
     * <li> lpop _key </li>
     *
     * @throws QueryTimeoutException 超时
     */
    public Object bLPop(String key, long timeout) {
        return listOperations.leftPop(key, timeout, TimeUnit.SECONDS);
    }

    /**
     * <h4> List </h4>
     * 弹出多个左边的值，若为空将会阻塞并等待其他客户端放入，超时后抛出异常
     * <li> lpop _key </li>
     *
     * @throws QueryTimeoutException 超时
     */
    public Object bLPop(String key, Duration timeout) {
        return listOperations.leftPop(key, timeout);
    }

    /**
     * <h4> List </h4>
     * 弹出一个右边的值
     * <li> lpop _key </li>
     */
    public Object rPop(String key) {
        return listOperations.rightPop(key);
    }

    /**
     * <h4> List </h4>
     * 弹出多个右边的值
     * <li> lpop _key </li>
     */
    public Object rPop(String key, long count) {
        return listOperations.rightPop(key, count);
    }

    /**
     * <h4> List </h4>
     * 弹出多个右边的值，若为空将会阻塞并等待其他客户端放入，超时后抛出异常
     * <li> lpop _key </li>
     *
     * @throws QueryTimeoutException 超时
     */
    public Object bRPop(String key, long timeout) {
        return listOperations.rightPop(key, timeout, TimeUnit.SECONDS);
    }

    /**
     * <h4> List </h4>
     * 弹出多个右边的值，若为空将会阻塞并等待其他客户端放入，超时后抛出异常
     * <li> lpop _key </li>
     *
     * @throws QueryTimeoutException 超时
     */
    public Object bRPop(String key, Duration timeout) {
        return listOperations.rightPop(key, timeout);
    }

    /**
     * <h4> List </h4>
     * 获取所有元素
     * <li> lrange _key 0 -1 </li>
     */
    public List<Object> lRange(String key) {
        return listOperations.range(key, 0, -1);
    }

    /**
     * <h4> List </h4>
     * 获取指定范围所有元素
     * <li> lrange _key _start _end </li>
     */
    public List<Object> lRange(String key, long start, long end) {
        return listOperations.range(key, start, end);
    }

    /**
     * <h4> List </h4>
     * 获取指定下标的元素
     * <li> lindex _key 0 -1 </li>
     */
    public Object lIndex(String key, long index) {
        return listOperations.index(key, index);
    }

    /**
     * <h4> List </h4>
     * 获取第一个指定元素的下标
     * <li> lpos _key _value </li>
     */
    public Long lPos(String key, Object value) {
        return listOperations.indexOf(key, value);
    }

    /**
     * <h4> List </h4>
     * 获取第 rank 个指定元素的下标
     * <li> lpos _key _value rank _rank </li>
     *
     * @param rank 可以为负数
     */
    public Long lPos(String key, Object value, Integer rank) {
        byte[] rowKey = rawKey(key);
        byte[] rowValue = rawValue(value);
        return redisTemplate.execute(connection -> {
            List<Long> indexes = connection.lPos(rowKey, rowValue, rank, null);
            return CollectionUtils.firstElement(indexes);
        }, true);
    }

    /**
     * <h4> List </h4>
     * 从第 rank 个指定元素开始，获取 count 个指定元素的下标
     * <li> lpos _key _value rank _rank count _count </li>
     *
     * @param rank 可以为负数
     */
    public List<Long> lPos(String key, Object value, Integer rank, Integer count) {
        byte[] rowKey = rawKey(key);
        byte[] rowValue = rawValue(value);
        return redisTemplate.execute(connection -> connection.lPos(rowKey, rowValue, rank, count), true);
    }

    /**
     * <h4> List </h4>
     * 删除 1 个指定元素
     * <li> lrem _key 1 _value </li>
     *
     * @return 成功删除数
     */
    public Long lRem(String key, Object value) {
        return listOperations.remove(key, 1, value);
    }

    /**
     * <h4> List </h4>
     * 删除 count 个指定元素
     * <li> lrem _key _count _value </li>
     *
     * @return 成功删除数
     */
    public Long lRem(String key, long count, Object value) {
        return listOperations.remove(key, count, value);
    }

    /**
     * <h4> List </h4>
     * 获取元素个数
     * <li> llen _key </li>
     */
    public Long lLen(String key) {
        return listOperations.size(key);
    }

    /**
     * <h4> List </h4>
     * 截取指定范围的元素，当 start 大于 end 并且同符号时删除所有
     * <li> ltrim _key _start _end </li>
     *
     * @param start 可以为负数（从右往左）
     * @param end   可以为负数（从右往左）
     */
    public void lTrim(String key, long start, long end) {
        listOperations.trim(key, start, end);
    }

}
